<!--
 * @Description:
 * @Author BPF
 * @create 2021/8/24 10:43
 -->
<template>
  <div class="page w-full relative vnc-component overflow-y-auto">
    <div
      style="
        width: 100%;
        height: 100%;
        background: rgb(40, 40, 40);
        display: flex;
        align-items: center;
        justify-content: center;
      "
      ref="vncWrap"
    >
      <transition name="el-fade-in" appear>
        <div class="flex flex-col h-full">
          <el-alert
            v-if="vncOptions.viewOnly"
            title="当前处于预览状态，不可操作"
            type="warning"
            show-icon
            close-text="知道了"
            class="z-100 flex-shrink-0"
          >
          </el-alert>
          <div
            v-if="isShow"
            id="screen"
            :style="{
              transition: '0.8s',
              opacity: rendering ? 0 : 1,
              position: 'relative',
              zIndex: 12,
              ..._vncStyle
            }"
          ></div>
        </div>
      </transition>
      <g-spin
        :loading="rendering"
        style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 10; color: #fff"
        loadingText="连接中"
        type="dot"
        size="default"
      ></g-spin>
    </div>

    <div
      class="control-wrap"
      ref="controlWrap"
      :style="{
        transform: visible.collapse ? 'translateX(50px)' : 'translateX(0)'
      }"
    >
      <div class="control-bar">
        <!-- 剪切板  -->
        <el-popover placement="right" width="450" trigger="click">
          <el-tooltip content="剪切板" :enterable="false" slot="reference">
            <div class="icon__item">
              <i class="text-16 el-icon-document-copy text-white"></i>
            </div>
          </el-tooltip>

          <div class="g-page-title"><i class="text-16 el-icon-document-copy pr-4"></i> 剪贴板</div>

          <el-input
            v-model="pasteText"
            type="textarea"
            :autosize="{ minRows: 4, maxRows: 10 }"
            placeholder="请输入剪切内容"
          >
          </el-input>

          <div class="flex justify-end py-6">
            <el-button size="mini" @click="pasteText = ''">清除</el-button>
          </div>
        </el-popover>

        <!-- 全屏  -->
        <div class="icon__item">
          <full-screen v-model="fullScreen"></full-screen>
        </div>

        <!-- 设置  -->
        <el-popover placement="right" width="400" trigger="click" popper-class="vnc-popper-setting">
          <el-tooltip content="设置" :enterable="false" slot="reference">
            <div class="icon__item">
              <i class="text-16 el-icon-setting text-white"></i>
            </div>
          </el-tooltip>
          <div class="vnc-popper-setting__body">
            <div class="g-page-title"><i class="text-16 el-icon-setting pr-4"></i> 设置</div>
            <el-divider content-position="left">🚀 基础设置</el-divider>
            <section class="other-setting__wrap" style="padding-bottom: 20px">
              <div class="setting__item">
                <span>仅查看：</span>
                <div>
                  <el-radio-group v-model="vncOptions.viewOnly">
                    <el-radio :label="item.value" v-for="item in options.common" :key="item.value">{{
                      item.label
                    }}</el-radio>
                  </el-radio-group>
                </div>
              </div>

              <div class="setting__item">
                <span>缩放模式：</span>
                <div>
                  <el-radio-group v-model="resizeMode" size="small" @change="onChangeResizeMode">
                    <el-radio :label="item.value" border v-for="item in options.resizeMode" :key="item.value">{{
                      item.label
                    }}</el-radio>
                  </el-radio-group>
                </div>
              </div>

              <div class="setting__item">
                <span>图像质量：</span>
                <div>
                  <el-select v-model="vncOptions.qualityLevel" placeholder="请选择">
                    <el-option
                      v-for="item in options.qualityLevel"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>
              <div class="setting__item">
                <span>图像压缩：</span>
                <div>
                  <el-select v-model="vncOptions.compressionLevel" placeholder="请选择">
                    <el-option
                      v-for="item in options.compressionLevel"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </div>
              </div>

              <div class="setting__item">
                <span>剪切窗口：</span>
                <div>
                  <el-radio-group v-model="vncOptions.clipViewport" size="small">
                    <el-radio :label="item.value" v-for="item in options.common" :key="item.value">{{
                      item.label
                    }}</el-radio>
                  </el-radio-group>
                </div>
              </div>
            </section>

            <el-divider content-position="left"> 🟢 WebSocket基础信息</el-divider>
            <section class="other-setting__wrap" style="padding-bottom: 20px">
              <el-form label-width="80px">
                <el-form-item label="主机："> {{ ip }}</el-form-item>
                <el-form-item label="端口："> {{ port }}</el-form-item>
                <el-form-item label="路径："> websockify</el-form-item>
              </el-form>
            </section>
          </div>
        </el-popover>

        <!-- 重新连接  -->
        <el-tooltip content="重新连接" :enterable="false">
          <div class="icon__item" @click="onLoadVnc">
            <i class="text-16 text-white el-icon-refresh"></i>
          </div>
        </el-tooltip>
      </div>

      <div class="control-arrow-wrap" @click="onCollapse">
        <div :class="['control-arrow']"></div>
        <img
          src="./imgs/handle.png"
          :style="{
            transform: visible.collapse ? 'rotate(0)' : 'rotate(180deg)'
          }"
        />
      </div>
    </div>
  </div>
</template>

<script>
import RFB from '@novnc/novnc/core/rfb'
import FullScreen from './components/fullscreen'
import BUtils from '@bianpengfei/utils'
export default {
  components: { FullScreen },
  props: {
    ip: {
      // ip地址
      type: String,
      default: '101.34.126.78'
    },
    port: {
      // 端口号
      type: [String, Number],
      default: 4800
    },
    pass: {
      // 密码
      type: String,
      default: '123456'
    },
    styles: {
      // vnc基础样式
      type: Object,
      default: () => ({
        width: '100vw',
        height: '100vh'
      })
    }
  },
  data() {
    return {
      isShow: true,
      rendering: true, // 是否渲染结束
      visible: {
        setting: false,
        collapse: false // 是否折叠
      },
      fullScreen: false, // 是否全屏
      resizeMode: 'scale',
      options: {
        resizeMode: [
          { value: 'scale', label: '本地缩放' },
          { value: 'remote', label: '远程调整大小' }
        ],
        qualityLevel: [...new Array(9)].map((v, vi) => {
          return {
            value: vi,
            label: `${vi + 1}级`
          }
        }),
        compressionLevel: [...new Array(9)].map((v, vi) => {
          return {
            value: vi,
            label: `${vi + 1}级`
          }
        }),
        common: [
          { value: true, label: '是' },
          { value: false, label: '否' }
        ]
      },
      vncStyle: {}, // vnc样式
      pasteText: '', // 粘贴内容
      vncOptions: {
        viewOnly: false, // 默认false，禁止向远程服务器发送任何鼠标或键盘事件，不能操作。
        scaleViewport: true, // 默认false，自动缩放远程会话适应容器。如果指定false，远程会话小于容器会居中显示，如果大于容器，根据clipViewport处理。
        resizeSession: false, // 是一个boolean指示是否每当容器改变尺寸应被发送到调整远程会话的请求。默认情况下禁用
        clipViewport: false, // 是boolean表示如果远程会话应该被剪辑到其容器。当禁用滚动条时将显示处理由此产生的溢出。默认禁用。
        qualityLevel: 8, // 是控制所需 JPEG 质量的int范围内[0-9]。价值0意味着低质量，也9意味着高质量。默认值为6。
        compressionLevel: 6, // 是控制所需压缩级别的int范围内[0-9]。值0意味着没有压缩。级别 1 使用最少的 CPU 资源并实现较弱的压缩率，而级别 9 提供最佳压缩，但在服务器端的 CPU 消耗方面较慢。在网络连接速度非常慢的情况下使用高级别。默认值为2。
        clickViewport: true, // 默认false，是否将远程会话窗口剪切来适配容器，如果为false，将通过滚动条处理溢出。
        dragViewport: false // 默认false，禁止向远程服务器发送任何鼠标或键盘事件，不能操作。
      }
    }
  },

  computed: {
    _vncStyle() {
      return {
        width: '100vw',
        height: '100vh',
        ...this.styles,
        ...this.vncStyle
      }
    }
  },

  watch: {
    pasteText: {
      handler(newVal) {
        this.onClipboardText(newVal)
      }
    },

    'vncOptions.qualityLevel'(newVal) {
      if (this._rfb) {
        this._rfb.qualityLevel = newVal
        this.onLoadVnc('update')
      }
    },

    'vncOptions.compressionLevel'(newVal) {
      if (this._rfb) {
        this._rfb.compressionLevel = newVal
        this.onLoadVnc('update')
      }
    },

    'vncOptions.viewOnly'(newVal) {
      if (this._rfb) {
        this._rfb.viewOnly = newVal
        this.onLoadVnc('update')
        if (newVal) {
          this.$nextTick(() => {
            this.$notify.warning({
              title: '警告',
              message: '当前处于预览状态，不可操作'
            })
          })
        }
      }
    },

    fullScreen: {
      handler() {
        this.$nextTick(() => {
          this.onLoadVnc('update')
        })
      }
    }
  },

  mounted() {
    // const res = await api.ftVncCreateVncUser()
    this.onLoadVnc()

    // 监听 ctrl+v键盘事件
    document.addEventListener('paste', (event) => {
      // 获取解析 粘贴的文本
      this.pasteText = (event.clipboardData || window.clipboardData).getData('text')
      if (this._rfb) {
        this._rfb.clipboardPasteFrom(this.pasteText)
      }
      console.log('监听到')
    })

    document.body.oncopy = function () {
      var copyText = window.getSelection ? window.getSelection() : document.selection.createRange().text
      alert(copyText)
    }

    window.addEventListener('beforeunload', (e) => {
      // window.navigator.sendBeacon()
    })

    // document.addEventListener('mousemove', )
  },
  methods: {
    /**
     * 连接vnc的函数
     * @param type 类型
     */
    connectVnc(type) {
      const url = `ws://${this.ip}:${this.port}`
      const rfb = new RFB(document.getElementById('screen'), url, {
        // 向vnc 传递的一些参数，比如说虚拟机的开机密码等
        credentials: { password: this.pass }
      })
      rfb.addEventListener('connect', this.connectedCallback.bind(type))
      rfb.addEventListener('disconnect', this.disconnectedCallback)
      rfb.addEventListener('clipboard', (arg1, arg2) => {
        console.log('clipboard', arg1, arg2)
      })

      Object.keys(this.vncOptions).forEach((key) => {
        rfb[key] = this.vncOptions[key]
      })

      console.log(rfb)
      this._rfb = rfb
    },

    /**
     * 重新加载vnc
     * @returns {Promise<unknown>}
     */
    onLoadVnc(type = 'reload') {
      return new Promise((resolve) => {
        this.isShow = false
        this.rendering = true
        this.$nextTick(() => {
          this.isShow = true
          this.$forceUpdate()
          setTimeout(() => {
            this.connectVnc.call(this, type)
            this.rendering = false
            resolve()
          }, 2000)
        })
      })
    },

    /**
     * vnc连接断开的回调函数
     * @param msg
     */
    disconnectedCallback(msg) {
      console.log(msg)
      this.$message.error('断开链接')
      if (msg.detail.clean) {
        // 根据 断开信息的msg.detail.clean 来判断是否可以重新连接
        this.connectVnc()
      } else {
        //这里做不可重新连接的一些操作
      }
    },

    /**
     * 连接成功的回调函数
     * @param type
     */
    connectedCallback(type = 'reload') {
      if (type == 'reload') {
        this.$notify.success({
          title: '提示',
          message: '连接成功'
        })
      } else if (type == 'update') {
        // this.$notify.success({
        //   title: '提示',
        //   message: '设置成功'
        // })
      }
    },

    /**
     * 粘贴板
     * @param text
     */
    @BUtils.debounceDtor(200)
    onClipboardText(text) {
      if (this._rfb) {
        this._rfb.clipboardPasteFrom(text)
      }
    },

    /**
     * 收缩/展开
     */
    onCollapse() {
      this.visible.collapse = !this.visible.collapse
    },

    /**
     * 缩放模式变化
     */
    onChangeResizeMode(val) {
      if (this._rfb) {
        this.vncOptions.scaleViewport = val === 'scale'
        this.vncOptions.resizeSession = val === 'remote'
        this.onLoadVnc('update')
      }
    },

    /**
     * 监听
     */
    @BUtils.throttleDtor(20)
    onMousemoveScreenRight() {}
  }
}
</script>

<style lang="scss" scoped>
@import './index.scss';
</style>

<style lang="scss">
.vnc-popper-setting {
  padding: 0;
  .vnc-popper-setting__body {
    max-height: 80vh;
    overflow: hidden auto;
    padding: 12px;
    //@extend .div--webkit--scrollbar-default;
  }
  .other-setting__wrap {
    .setting__item {
      display: flex;
      justify-content: space-between;
      padding: 10px;
      color: rgba(0, 0, 0, 0.65);
      font-size: 14px;
      & > span:nth-child(1) {
        font-weight: bold;
      }
      .el-radio:not(.is-bordered) {
        margin-right: 12px;
      }
      .el-radio.is-bordered {
        margin-right: 0;
      }
    }
  }
}
</style>
